<!DOCTYPE html>
<html>
  <head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
  <meta name="description" content="刘清政">
  <meta name="keyword" content="hexo-theme">
  
    <link rel="shortcut icon" href="/css/images/logo.png">
  
  <title>
    
      22-信道（channel） | Justin-刘清政的博客
    
  </title>
  <link href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <link href="//cdnjs.cloudflare.com/ajax/libs/nprogress/0.2.0/nprogress.min.css" rel="stylesheet">
  <link href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/tomorrow.min.css" rel="stylesheet">
  
<link rel="stylesheet" href="/css/style.css">

  
    
<link rel="stylesheet" href="/css/plugins/gitment.css">

  
  <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/geopattern/1.2.3/js/geopattern.min.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/nprogress/0.2.0/nprogress.min.js"></script>
  
    
<script src="/js/qrious.js"></script>

  
  
    
<script src="/js/gitment.js"></script>

  
  

  
<meta name="generator" content="Hexo 4.2.0"></head>
<div class="wechat-share">
  <img src="/css/images/logo.png" />
</div>

  <body>
    <header class="header fixed-header">
  <div class="header-container">
    <a class="home-link" href="/">
      <div class="logo"></div>
      <span>Justin-刘清政的博客</span>
    </a>
    <ul class="right-list">
      
        <li class="list-item">
          
            <a href="/" class="item-link">主页</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/tags/" class="item-link">标签</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/archives/" class="item-link">归档</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/about/" class="item-link">关于我</a>
          
        </li>
      
    </ul>
    <div class="menu">
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
    </div>
    <div class="menu-mask">
      <ul class="menu-list">
        
          <li class="menu-item">
            
              <a href="/" class="menu-link">主页</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/tags/" class="menu-link">标签</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/archives/" class="menu-link">归档</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/about/" class="menu-link">关于我</a>
            
          </li>
        
      </ul>
    </div>
  </div>
</header>

    <div id="article-banner">
  <h2>22-信道（channel）</h2>



  <p class="post-date">2019-03-10</p>
    <!-- 不蒜子统计 -->
    <span id="busuanzi_container_page_pv" style='display:none' class="">
        <i class="icon-smile icon"></i> 阅读数：<span id="busuanzi_value_page_pv"></span>次
    </span>
  <div class="arrow-down">
    <a href="javascript:;"></a>
  </div>
</div>
<main class="app-body flex-box">
  <!-- Article START -->
  <article class="post-article">
    <section class="markdown-content"><h2 id="什么是信道？"><a href="#什么是信道？" class="headerlink" title="什么是信道？"></a>什么是信道？</h2><p>信道可以想像成 Go 协程之间通信的管道。如同管道中的水会从一端流到另一端，通过使用信道，数据也可以从一端发送，在另一端接收。  </p>
<h2 id="信道的声明"><a href="#信道的声明" class="headerlink" title="信道的声明"></a>信道的声明</h2><p>所有信道都关联了一个类型。信道只能运输这种类型的数据，而运输其他类型的数据都是非法的。  </p>
<p><code>chan T</code> 表示 <code>T</code> 类型的信道。  </p>
<p>信道的零值为 <code>nil</code>。信道的零值没有什么用，应该像对 map 和切片所做的那样，用 <code>make</code> 来定义信道。  </p>
<p>下面编写代码，声明一个信道。  </p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;  </span><br><span class="line">    <span class="keyword">var</span> a <span class="keyword">chan</span> <span class="keyword">int</span></span><br><span class="line">    <span class="keyword">if</span> a == <span class="literal">nil</span> &#123;</span><br><span class="line">        fmt.Println(<span class="string">"channel a is nil, going to define it"</span>)</span><br><span class="line">        a = <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line">        fmt.Printf(<span class="string">"Type of a is %T"</span>, a)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>由于信道的零值为 <code>nil</code>，在第 6 行，信道 <code>a</code> 的值就是 <code>nil</code>。于是，程序执行了 if 语句内的语句，定义了信道 <code>a</code>。程序中 <code>a</code> 是一个 int 类型的信道。该程序会输出：  </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">channel a is nil, going to define it  </span><br><span class="line">Type of a is chan int</span><br></pre></td></tr></table></figure>

<p>简短声明通常也是一种定义信道的简洁有效的方法。  </p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">a := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br></pre></td></tr></table></figure>

<p>这一行代码同样定义了一个 int 类型的信道 <code>a</code>。  </p>
<h2 id="通过信道进行发送和接收"><a href="#通过信道进行发送和接收" class="headerlink" title="通过信道进行发送和接收"></a>通过信道进行发送和接收</h2><p>如下所示，该语法通过信道发送和接收数据。  </p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">data := &lt;- a <span class="comment">// 读取信道 a  </span></span><br><span class="line">a &lt;- data <span class="comment">// 写入信道 a</span></span><br></pre></td></tr></table></figure>

<p>信道旁的箭头方向指定了是发送数据还是接收数据。  </p>
<p>在第一行，箭头对于 <code>a</code> 来说是向外指的，因此我们读取了信道 <code>a</code> 的值，并把该值存储到变量 <code>data</code>。  </p>
<p>在第二行，箭头指向了 <code>a</code>，因此我们在把数据写入信道 <code>a</code>。  </p>
<h2 id="发送与接收默认是阻塞的"><a href="#发送与接收默认是阻塞的" class="headerlink" title="发送与接收默认是阻塞的"></a>发送与接收默认是阻塞的</h2><p>发送与接收默认是阻塞的。这是什么意思？当把数据发送到信道时，程序控制会在发送数据的语句处发生阻塞，直到有其它 Go 协程从信道读取到数据，才会解除阻塞。与此类似，当读取信道的数据时，如果没有其它的协程把数据写入到这个信道，那么读取过程就会一直阻塞着。  </p>
<p>信道的这种特性能够帮助 Go 协程之间进行高效的通信，不需要用到其他编程语言常见的显式锁或条件变量。  </p>
<h2 id="信道的代码示例"><a href="#信道的代码示例" class="headerlink" title="信道的代码示例"></a>信道的代码示例</h2><p>理论已经够了:)。接下来写点代码，看看协程之间通过信道是怎么通信的吧。  </p>
<p>我们其实可以重写上章学习 [Go 协程]时写的程序，现在我们在这里用上信道。  </p>
<p>首先引用前面教程里的程序。  </p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (  </span><br><span class="line">    <span class="string">"fmt"</span></span><br><span class="line">    <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">hello</span><span class="params">()</span></span> &#123;  </span><br><span class="line">    fmt.Println(<span class="string">"Hello world goroutine"</span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;  </span><br><span class="line">    <span class="keyword">go</span> hello()</span><br><span class="line">    time.Sleep(<span class="number">1</span> * time.Second)</span><br><span class="line">    fmt.Println(<span class="string">"main function"</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这是上一篇的代码。我们使用到了休眠，使 Go 主协程等待 hello 协程结束。如果你看不懂，建议你阅读上一教程 [Go 协程]。  </p>
<p>我们接下来使用信道来重写上面代码。  </p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (  </span><br><span class="line">    <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">hello</span><span class="params">(done <span class="keyword">chan</span> <span class="keyword">bool</span>)</span></span> &#123;  </span><br><span class="line">    fmt.Println(<span class="string">"Hello world goroutine"</span>)</span><br><span class="line">    done &lt;- <span class="literal">true</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;  </span><br><span class="line">    done := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">bool</span>)</span><br><span class="line">    <span class="keyword">go</span> hello(done)</span><br><span class="line">    &lt;-done</span><br><span class="line">    fmt.Println(<span class="string">"main function"</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在上述程序里，我们在第 12 行创建了一个 bool 类型的信道 <code>done</code>，并把 <code>done</code> 作为参数传递给了 <code>hello</code> 协程。在第 14 行，我们通过信道 <code>done</code> 接收数据。这一行代码发生了阻塞，除非有协程向 <code>done</code> 写入数据，否则程序不会跳到下一行代码。于是，这就不需要用以前的 <code>time.Sleep</code> 来阻止 Go 主协程退出了。  </p>
<p><code>&lt;-done</code> 这行代码通过协程（译注：原文笔误，信道）<code>done</code> 接收数据，但并没有使用数据或者把数据存储到变量中。这完全是合法的。  </p>
<p>现在我们的 Go 主协程发生了阻塞，等待信道 <code>done</code> 发送的数据。该信道作为参数传递给了协程 <code>hello</code>，<code>hello</code> 打印出 <code>Hello world goroutine</code>，接下来向 <code>done</code> 写入数据。当完成写入时，Go 主协程会通过信道 <code>done</code> 接收数据，于是它解除阻塞状态，打印出文本 <code>main function</code>。  </p>
<p>该程序输出如下：  </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Hello world goroutine  </span><br><span class="line">main function</span><br></pre></td></tr></table></figure>

<p>我们稍微修改一下程序，在 <code>hello</code> 协程里加入休眠函数，以便更好地理解阻塞的概念。  </p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (  </span><br><span class="line">    <span class="string">"fmt"</span></span><br><span class="line">    <span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">hello</span><span class="params">(done <span class="keyword">chan</span> <span class="keyword">bool</span>)</span></span> &#123;  </span><br><span class="line">    fmt.Println(<span class="string">"hello go routine is going to sleep"</span>)</span><br><span class="line">    time.Sleep(<span class="number">4</span> * time.Second)</span><br><span class="line">    fmt.Println(<span class="string">"hello go routine awake and going to write to done"</span>)</span><br><span class="line">    done &lt;- <span class="literal">true</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;  </span><br><span class="line">    done := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">bool</span>)</span><br><span class="line">    fmt.Println(<span class="string">"Main going to call hello go goroutine"</span>)</span><br><span class="line">    <span class="keyword">go</span> hello(done)</span><br><span class="line">    &lt;-done</span><br><span class="line">    fmt.Println(<span class="string">"Main received data"</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在上面程序里，我们向 <code>hello</code> 函数里添加了 4 秒的休眠（第 10 行）。  </p>
<p>程序首先会打印 <code>Main going to call hello go goroutine</code>。接着会开启 <code>hello</code> 协程，打印 <code>hello go routine is going to sleep</code>。打印完之后，<code>hello</code> 协程会休眠 4 秒钟，而在这期间，主协程会在 <code>&lt;-done</code> 这一行发生阻塞，等待来自信道 <code>done</code> 的数据。4 秒钟之后，打印 <code>hello go routine awake and going to write to done</code>，接着再打印 <code>Main received data</code>。  </p>
<h2 id="信道的另一个示例"><a href="#信道的另一个示例" class="headerlink" title="信道的另一个示例"></a>信道的另一个示例</h2><p>我们再编写一个程序来更好地理解信道。该程序会计算一个数中每一位的平方和与立方和，然后把平方和与立方和相加并打印出来。</p>
<p>例如，如果输出是 123，该程序会如下计算输出：  </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">squares &#x3D; (1 * 1) + (2 * 2) + (3 * 3) </span><br><span class="line">cubes &#x3D; (1 * 1 * 1) + (2 * 2 * 2) + (3 * 3 * 3) </span><br><span class="line">output &#x3D; squares + cubes &#x3D; 50</span><br></pre></td></tr></table></figure>

<p>我们会这样去构建程序：在一个单独的 Go 协程计算平方和，而在另一个协程计算立方和，最后在 Go 主协程把平方和与立方和相加。  </p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (  </span><br><span class="line">    <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">calcSquares</span><span class="params">(number <span class="keyword">int</span>, squareop <span class="keyword">chan</span> <span class="keyword">int</span>)</span></span> &#123;  </span><br><span class="line">    sum := <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> number != <span class="number">0</span> &#123;</span><br><span class="line">        digit := number % <span class="number">10</span></span><br><span class="line">        sum += digit * digit</span><br><span class="line">        number /= <span class="number">10</span></span><br><span class="line">    &#125;</span><br><span class="line">    squareop &lt;- sum</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">calcCubes</span><span class="params">(number <span class="keyword">int</span>, cubeop <span class="keyword">chan</span> <span class="keyword">int</span>)</span></span> &#123;  </span><br><span class="line">    sum := <span class="number">0</span> </span><br><span class="line">    <span class="keyword">for</span> number != <span class="number">0</span> &#123;</span><br><span class="line">        digit := number % <span class="number">10</span></span><br><span class="line">        sum += digit * digit * digit</span><br><span class="line">        number /= <span class="number">10</span></span><br><span class="line">    &#125;</span><br><span class="line">    cubeop &lt;- sum</span><br><span class="line">&#125; </span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;  </span><br><span class="line">    number := <span class="number">589</span></span><br><span class="line">    sqrch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line">    cubech := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line">    <span class="keyword">go</span> calcSquares(number, sqrch)</span><br><span class="line">    <span class="keyword">go</span> calcCubes(number, cubech)</span><br><span class="line">    squares, cubes := &lt;-sqrch, &lt;-cubech</span><br><span class="line">    fmt.Println(<span class="string">"Final output"</span>, squares + cubes)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在第 7 行，函数 <code>calcSquares</code> 计算一个数每位的平方和，并把结果发送给信道 <code>squareop</code>。与此类似，在第 17 行函数 <code>calcCubes</code> 计算一个数每位的立方和，并把结果发送给信道 <code>cubop</code>。  </p>
<p>这两个函数分别在单独的协程里运行（第 31 行和第 32 行），每个函数都有传递信道的参数，以便写入数据。Go 主协程会在第 33 行等待两个信道传来的数据。一旦从两个信道接收完数据，数据就会存储在变量 <code>squares</code> 和 <code>cubes</code> 里，然后计算并打印出最后结果。该程序会输出：  </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Final output 1536</span><br></pre></td></tr></table></figure>

<h2 id="死锁"><a href="#死锁" class="headerlink" title="死锁"></a>死锁</h2><p>使用信道需要考虑的一个重点是死锁。当 Go 协程给一个信道发送数据时，照理说会有其他 Go 协程来接收数据。如果没有的话，程序就会在运行时触发 panic，形成死锁。  </p>
<p>同理，当有 Go 协程等着从一个信道接收数据时，我们期望其他的 Go 协程会向该信道写入数据，要不然程序就会触发 panic。 </p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;  </span><br><span class="line">    ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line">    ch &lt;- <span class="number">5</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在上述程序中，我们创建了一个信道 <code>ch</code>，接着在下一行 <code>ch &lt;- 5</code>，我们把 <code>5</code> 发送到这个信道。对于本程序，没有其他的协程从 <code>ch</code> 接收数据。于是程序触发 panic，出现如下运行时错误。  </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">fatal error: all goroutines are asleep - deadlock!</span><br><span class="line"></span><br><span class="line">goroutine 1 [chan send]:  </span><br><span class="line">main.main()  </span><br><span class="line">    &#x2F;tmp&#x2F;sandbox249677995&#x2F;main.go:6 +0x80</span><br></pre></td></tr></table></figure>

<h2 id="单向信道"><a href="#单向信道" class="headerlink" title="单向信道"></a>单向信道</h2><p>我们目前讨论的信道都是双向信道，即通过信道既能发送数据，又能接收数据。其实也可以创建单向信道，这种信道只能发送或者接收数据。  </p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">sendData</span><span class="params">(sendch <span class="keyword">chan</span>&lt;- <span class="keyword">int</span>)</span></span> &#123;  </span><br><span class="line">    sendch &lt;- <span class="number">10</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;  </span><br><span class="line">    sendch := <span class="built_in">make</span>(<span class="keyword">chan</span>&lt;- <span class="keyword">int</span>)</span><br><span class="line">    <span class="keyword">go</span> sendData(sendch)</span><br><span class="line">    fmt.Println(&lt;-sendch)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面程序的第 10 行，我们创建了唯送（Send Only）信道 <code>sendch</code>。<code>chan&lt;- int</code> 定义了唯送信道，因为箭头指向了 <code>chan</code>。在第 12 行，我们试图通过唯送信道接收数据，于是编译器报错：  </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">main.go:11: invalid operation: &lt;-sendch (receive from send-only type chan&lt;- int)</span><br></pre></td></tr></table></figure>

<p><strong>一切都很顺利，只不过一个不能读取数据的唯送信道究竟有什么意义呢？</strong>  </p>
<p><strong>这就需要用到信道转换（Channel Conversion）了。把一个双向信道转换成唯送信道或者唯收（Receive Only）信道都是行得通的，但是反过来就不行。</strong>  </p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">sendData</span><span class="params">(sendch <span class="keyword">chan</span>&lt;- <span class="keyword">int</span>)</span></span> &#123;  </span><br><span class="line">    sendch &lt;- <span class="number">10</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;  </span><br><span class="line">    cha1 := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line">    <span class="keyword">go</span> sendData(cha1)</span><br><span class="line">    fmt.Println(&lt;-cha1)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在上述程序的第 10 行，我们创建了一个双向信道 <code>cha1</code>。在第 11 行 <code>cha1</code> 作为参数传递给了 <code>sendData</code> 协程。在第 5 行，函数 <code>sendData</code> 里的参数 <code>sendch chan&lt;- int</code> 把 <code>cha1</code> 转换为一个唯送信道。于是该信道在 <code>sendData</code> 协程里是一个唯送信道，而在 Go 主协程里是一个双向信道。该程序最终打印输出 <code>10</code>。  </p>
<h2 id="关闭信道和使用-for-range-遍历信道"><a href="#关闭信道和使用-for-range-遍历信道" class="headerlink" title="关闭信道和使用 for range 遍历信道"></a>关闭信道和使用 for range 遍历信道</h2><p>数据发送方可以关闭信道，通知接收方这个信道不再有数据发送过来。  </p>
<p>当从信道接收数据时，接收方可以多用一个变量来检查信道是否已经关闭。  </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">v, ok :&#x3D; &lt;- ch</span><br></pre></td></tr></table></figure>

<p>上面的语句里，如果成功接收信道所发送的数据，那么 <code>ok</code> 等于 true。而如果 <code>ok</code> 等于 false，说明我们试图读取一个关闭的通道。从关闭的信道读取到的值会是该信道类型的零值。例如，当信道是一个 <code>int</code> 类型的信道时，那么从关闭的信道读取的值将会是 <code>0</code>。  </p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (  </span><br><span class="line">    <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">producer</span><span class="params">(chnl <span class="keyword">chan</span> <span class="keyword">int</span>)</span></span> &#123;  </span><br><span class="line">    <span class="keyword">for</span> i := <span class="number">0</span>; i &lt; <span class="number">10</span>; i++ &#123;</span><br><span class="line">        chnl &lt;- i</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">close</span>(chnl)</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;  </span><br><span class="line">    ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line">    <span class="keyword">go</span> producer(ch)</span><br><span class="line">    <span class="keyword">for</span> &#123;</span><br><span class="line">        v, ok := &lt;-ch</span><br><span class="line">        <span class="keyword">if</span> ok == <span class="literal">false</span> &#123;</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        &#125;</span><br><span class="line">        fmt.Println(<span class="string">"Received "</span>, v, ok)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在上述的程序中，<code>producer</code> 协程会从 0 到 9 写入信道 <code>chn1</code>，然后关闭该信道。主函数有一个无限的 for 循环（第 16 行），使用变量 <code>ok</code>（第 18 行）检查信道是否已经关闭。如果 <code>ok</code> 等于 false，说明信道已经关闭，于是退出 for 循环。如果 <code>ok</code> 等于 true，会打印出接收到的值和 <code>ok</code> 的值。  </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">Received  0 true  </span><br><span class="line">Received  1 true  </span><br><span class="line">Received  2 true  </span><br><span class="line">Received  3 true  </span><br><span class="line">Received  4 true  </span><br><span class="line">Received  5 true  </span><br><span class="line">Received  6 true  </span><br><span class="line">Received  7 true  </span><br><span class="line">Received  8 true  </span><br><span class="line">Received  9 true</span><br></pre></td></tr></table></figure>

<p>for range 循环用于在一个信道关闭之前，从信道接收数据。  </p>
<p>接下来我们使用 for range 循环重写上面的代码。  </p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (  </span><br><span class="line">    <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">producer</span><span class="params">(chnl <span class="keyword">chan</span> <span class="keyword">int</span>)</span></span> &#123;  </span><br><span class="line">    <span class="keyword">for</span> i := <span class="number">0</span>; i &lt; <span class="number">10</span>; i++ &#123;</span><br><span class="line">        chnl &lt;- i</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">close</span>(chnl)</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;  </span><br><span class="line">    ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line">    <span class="keyword">go</span> producer(ch)</span><br><span class="line">    <span class="keyword">for</span> v := <span class="keyword">range</span> ch &#123;</span><br><span class="line">        fmt.Println(<span class="string">"Received "</span>,v)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在第 16 行，for range 循环从信道 <code>ch</code> 接收数据，直到该信道关闭。一旦关闭了 <code>ch</code>，循环会自动结束。该程序会输出：  </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">Received  0  </span><br><span class="line">Received  1  </span><br><span class="line">Received  2  </span><br><span class="line">Received  3  </span><br><span class="line">Received  4  </span><br><span class="line">Received  5  </span><br><span class="line">Received  6  </span><br><span class="line">Received  7  </span><br><span class="line">Received  8  </span><br><span class="line">Received  9</span><br></pre></td></tr></table></figure>

<p>我们可以使用 for range 循环，重写[信道的另一个示例]这一节里面的代码，提高代码的可重用性。  </p>
<p>如果你仔细观察这段代码，会发现获得一个数里的每位数的代码在 <code>calcSquares</code> 和 <code>calcCubes</code> 两个函数内重复了。我们将把这段代码抽离出来，放在一个单独的函数里，然后并发地调用它。  </p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (  </span><br><span class="line">    <span class="string">"fmt"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">digits</span><span class="params">(number <span class="keyword">int</span>, dchnl <span class="keyword">chan</span> <span class="keyword">int</span>)</span></span> &#123;  </span><br><span class="line">    <span class="keyword">for</span> number != <span class="number">0</span> &#123;</span><br><span class="line">        digit := number % <span class="number">10</span></span><br><span class="line">        dchnl &lt;- digit</span><br><span class="line">        number /= <span class="number">10</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">close</span>(dchnl)</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">calcSquares</span><span class="params">(number <span class="keyword">int</span>, squareop <span class="keyword">chan</span> <span class="keyword">int</span>)</span></span> &#123;  </span><br><span class="line">    sum := <span class="number">0</span></span><br><span class="line">    dch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line">    <span class="keyword">go</span> digits(number, dch)</span><br><span class="line">    <span class="keyword">for</span> digit := <span class="keyword">range</span> dch &#123;</span><br><span class="line">        sum += digit * digit</span><br><span class="line">    &#125;</span><br><span class="line">    squareop &lt;- sum</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">calcCubes</span><span class="params">(number <span class="keyword">int</span>, cubeop <span class="keyword">chan</span> <span class="keyword">int</span>)</span></span> &#123;  </span><br><span class="line">    sum := <span class="number">0</span></span><br><span class="line">    dch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line">    <span class="keyword">go</span> digits(number, dch)</span><br><span class="line">    <span class="keyword">for</span> digit := <span class="keyword">range</span> dch &#123;</span><br><span class="line">        sum += digit * digit * digit</span><br><span class="line">    &#125;</span><br><span class="line">    cubeop &lt;- sum</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;  </span><br><span class="line">    number := <span class="number">589</span></span><br><span class="line">    sqrch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line">    cubech := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line">    <span class="keyword">go</span> calcSquares(number, sqrch)</span><br><span class="line">    <span class="keyword">go</span> calcCubes(number, cubech)</span><br><span class="line">    squares, cubes := &lt;-sqrch, &lt;-cubech</span><br><span class="line">    fmt.Println(<span class="string">"Final output"</span>, squares+cubes)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上述程序里的 <code>digits</code> 函数，包含了获取一个数的每位数的逻辑，并且 <code>calcSquares</code> 和 <code>calcCubes</code> 两个函数并发地调用了 <code>digits</code>。当计算完数字里面的每一位数时，第 13 行就会关闭信道。<code>calcSquares</code> 和 <code>calcCubes</code> 两个协程使用 for range 循环分别监听了它们的信道，直到该信道关闭。程序的其他地方不变，该程序同样会输出：  </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Final output 1536</span><br></pre></td></tr></table></figure>

<p>关于信道还有一些其他的概念，比如缓冲信道（Buffered Channel）、工作池（Worker Pool）和 select。我们会在接下来的教程里专门介绍它们</p>
</section>
    <!-- Tags START -->
    
      <div class="tags">
        <span>Tags:</span>
        
  <a href="/tags#Go" >
    <span class="tag-code">Go</span>
  </a>

      </div>
    
    <!-- Tags END -->
    <!-- NAV START -->
    
  <div class="nav-container">
    <!-- reverse left and right to put prev and next in a more logic postition -->
    
      <a class="nav-left" href="/Elasticsearch/Elasticsearch%E7%B3%BB%E5%88%97%E6%95%99%E7%A8%8B/">
        <span class="nav-arrow">← </span>
        
          [置顶]Elasticsearch系列教程
        
      </a>
    
    
      <a class="nav-right" href="/linux/Linux%E7%B3%BB%E5%88%97%E6%95%99%E7%A8%8B/">
        
          [置顶]Linux系列教程
        
        <span class="nav-arrow"> →</span>
      </a>
    
  </div>

    <!-- NAV END -->
    <!-- 打赏 START -->
    
      <div class="money-like">
        <div class="reward-btn">
          赏
          <span class="money-code">
            <span class="alipay-code">
              <div class="code-image"></div>
              <b>使用支付宝打赏</b>
            </span>
            <span class="wechat-code">
              <div class="code-image"></div>
              <b>使用微信打赏</b>
            </span>
          </span>
        </div>
        <p class="notice">点击上方按钮,请我喝杯咖啡！</p>
      </div>
    
    <!-- 打赏 END -->
    <!-- 二维码 START -->
    
      <div class="qrcode">
        <canvas id="share-qrcode"></canvas>
        <p class="notice">扫描二维码，分享此文章</p>
      </div>
    
    <!-- 二维码 END -->
    
      <!-- Gitment START -->
      <div id="comments"></div>
      <!-- Gitment END -->
    
  </article>
  <!-- Article END -->
  <!-- Catalog START -->
  
    <aside class="catalog-container">
  <div class="toc-main">
  <!-- 不蒜子统计 -->
    <strong class="toc-title">目录</strong>
    
      <ol class="toc-nav"><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#什么是信道？"><span class="toc-nav-text">什么是信道？</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#信道的声明"><span class="toc-nav-text">信道的声明</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#通过信道进行发送和接收"><span class="toc-nav-text">通过信道进行发送和接收</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#发送与接收默认是阻塞的"><span class="toc-nav-text">发送与接收默认是阻塞的</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#信道的代码示例"><span class="toc-nav-text">信道的代码示例</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#信道的另一个示例"><span class="toc-nav-text">信道的另一个示例</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#死锁"><span class="toc-nav-text">死锁</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#单向信道"><span class="toc-nav-text">单向信道</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#关闭信道和使用-for-range-遍历信道"><span class="toc-nav-text">关闭信道和使用 for range 遍历信道</span></a></li></ol>
    
  </div>
</aside>
  
  <!-- Catalog END -->
</main>

<script>
  (function () {
    var url = 'http://www.liuqingzheng.top/go/2-Go语言基础/22-信道（channel）/';
    var banner = ''
    if (banner !== '' && banner !== 'undefined' && banner !== 'null') {
      $('#article-banner').css({
        'background-image': 'url(' + banner + ')'
      })
    } else {
      $('#article-banner').geopattern(url)
    }
    $('.header').removeClass('fixed-header')

    // error image
    $(".markdown-content img").on('error', function() {
      $(this).attr('src', 'http://file.muyutech.com/error-img.png')
      $(this).css({
        'cursor': 'default'
      })
    })

    // zoom image
    $(".markdown-content img").on('click', function() {
      var src = $(this).attr('src')
      if (src !== 'http://file.muyutech.com/error-img.png') {
        var imageW = $(this).width()
        var imageH = $(this).height()

        var zoom = ($(window).width() * 0.95 / imageW).toFixed(2)
        zoom = zoom < 1 ? 1 : zoom
        zoom = zoom > 2 ? 2 : zoom
        var transY = (($(window).height() - imageH) / 2).toFixed(2)

        $('body').append('<div class="image-view-wrap"><div class="image-view-inner"><img src="'+ src +'" /></div></div>')
        $('.image-view-wrap').addClass('wrap-active')
        $('.image-view-wrap img').css({
          'width': `${imageW}`,
          'transform': `translate3d(0, ${transY}px, 0) scale3d(${zoom}, ${zoom}, 1)`
        })
        $('html').css('overflow', 'hidden')

        $('.image-view-wrap').on('click', function() {
          $(this).remove()
          $('html').attr('style', '')
        })
      }
    })
  })();
</script>


  <script>
    var qr = new QRious({
      element: document.getElementById('share-qrcode'),
      value: document.location.href
    });
  </script>



  <script>
    var gitmentConfig = "liuqingzheng";
    if (gitmentConfig !== 'undefined') {
      var gitment = new Gitment({
        id: "22-信道（channel）",
        owner: "liuqingzheng",
        repo: "FuckBlog",
        oauth: {
          client_id: "32a4076431cf39d0ecea",
          client_secret: "94484bd79b3346a949acb2fda3c8a76ce16990c6"
        },
        theme: {
          render(state, instance) {
            const container = document.createElement('div')
            container.lang = "en-US"
            container.className = 'gitment-container gitment-root-container'
            container.appendChild(instance.renderHeader(state, instance))
            container.appendChild(instance.renderEditor(state, instance))
            container.appendChild(instance.renderComments(state, instance))
            container.appendChild(instance.renderFooter(state, instance))
            return container;
          }
        }
      })
      gitment.render(document.getElementById('comments'))
    }
  </script>




    <div class="scroll-top">
  <span class="arrow-icon"></span>
</div>
    <footer class="app-footer">
<!-- 不蒜子统计 -->
<span id="busuanzi_container_site_pv">
     本站总访问量<span id="busuanzi_value_site_pv"></span>次
</span>
<span class="post-meta-divider">|</span>
<span id="busuanzi_container_site_uv" style='display:none'>
     本站访客数<span id="busuanzi_value_site_uv"></span>人
</span>
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>



  <p class="copyright">
    &copy; 2021 | Proudly powered by <a href="https://www.cnblogs.com/xiaoyuanqujing" target="_blank">小猿取经</a>
    <br>
    Theme by <a href="https://www.cnblogs.com/xiaoyuanqujing" target="_blank" rel="noopener">小猿取经</a>
  </p>
</footer>

<script>
  function async(u, c) {
    var d = document, t = 'script',
      o = d.createElement(t),
      s = d.getElementsByTagName(t)[0];
    o.src = u;
    if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
    s.parentNode.insertBefore(o, s);
  }
</script>
<script>
  async("//cdnjs.cloudflare.com/ajax/libs/fastclick/1.0.6/fastclick.min.js", function(){
    FastClick.attach(document.body);
  })
</script>

<script>
  var hasLine = 'true';
  async("//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js", function(){
    $('figure pre').each(function(i, block) {
      var figure = $(this).parents('figure');
      if (hasLine === 'false') {
        figure.find('.gutter').hide();
      }
      var lang = figure.attr('class').split(' ')[1] || 'code';
      var codeHtml = $(this).html();
      var codeTag = document.createElement('code');
      codeTag.className = lang;
      codeTag.innerHTML = codeHtml;
      $(this).attr('class', '').empty().html(codeTag);
      figure.attr('data-lang', lang.toUpperCase());
      hljs.highlightBlock(block);
    });
  })
</script>





<!-- Baidu Tongji -->

<script>
    var _baId = 'c5fd96eee1193585be191f318c3fa725';
    // Originial
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "//hm.baidu.com/hm.js?" + _baId;
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
</script>


<script src="/js/script.js"></script>


<script src="/js/search.js"></script>


<script src="/js/load.js"></script>



  <span class="local-search local-search-google local-search-plugin" style="right: 50px;top: 70px;;position:absolute;z-index:2;">
      <input type="search" placeholder="站内搜索" id="local-search-input" class="local-search-input-cls" style="">
      <div id="local-search-result" class="local-search-result-cls"></div>
  </span>


  </body>
</html>